/*
 * Copyright (c) 2012-2012 XXXX XXXXXX, Inc.
 * XXXX Road, XXXX, XXXX State, XXXX Post code, XXXX Country.
 * All rights reserved.
 *
 * This software is the confidential and proprietary information of XXXX
 * XXXXXX, Inc. ("Confidential Information").  You shall not
 * disclose such Confidential Information and shall use it only in
 * accordance with the terms of the license agreement you entered into
 * with XXXX.
 * 
 * History
 * Date        Ver Author        Change Description
 * ----------- --- ------------- ----------------------------------------------
 * 04 Jun 2012 001 Karl          Create
 * 10 Jul 2012 002 Karl          BIP2.5
 * 
 */

package com.selenium.test.base;

import java.util.HashMap;
import java.util.Map;
import org.openqa.selenium.By;
import org.openqa.selenium.TimeoutException;
import com.selenium.test.util.ISeleniumUtil;
import com.selenium.test.util.SeleniumUtil;

/**
 * Page stands for a page that browser prompted out after selenium calls 
 * get(URL) or click a link, etc. Generally speaking, anything used to describe 
 * a page can be put into a Page class, such as default "wait time for page to 
 * load" variable, link logic names, etc. 
 * 
 */
public abstract class Page {
	/*--------------------1. page defined here--------------------------------*/
	protected ISeleniumUtil seleniumUtil = null;
	/** 
	 * By default after assign member variables, page uses a wait strategy to 
	 * make sure web page is stable already.
	 */
	protected WaitForPageToLoadStrategy waitStrategy = 
			new WaitForPageToLoadNull();
	
	/** Auto initialize element map*/
	protected Map<String, Object> initElements = new HashMap<String, Object>();
	
	/**
	 * Page constructor steps: <br/>
	 * 1. assignment of member variables <br/>
	 * 2. post assignment, some other assignment etc for sub class to override
	 *    <br/>
	 * 3. wait for page to load <br/>
	 * 4. operations after "wait for page to load" operations, 
	 *    such as : initialize elements in current page <br/>
	 * 
	 * @param seleniumUtil selenium utility must be given
	 * @param waitStrategy a user defined wait strategy
	 */
	public Page(ISeleniumUtil seleniumUtil, 
			WaitForPageToLoadStrategy waitStrategy){
		this.seleniumUtil = seleniumUtil;
		this.waitStrategy = waitStrategy;
		waitForPageToLoad();
	}
	
	/** first make sure page load correctly, then check user defined wait*/
	public void waitForPageToLoad(){
		seleniumUtil.waitForPageToLoad();
		seleniumUtil.waitForPageToLoad(waitStrategy);
	}
	
	/**
	 * @param seleniumUtil selenium utility must be given
	 */
	public Page(ISeleniumUtil seleniumUtil){
		// if not given wait-on-locators, return directly
		this(seleniumUtil, new WaitForPageToLoadNull());
	}
	
	/**
	 * @param seleniumUtil selenium utility must be given
	 * @param waitOnLocators array of locators which are declared to be 
	 *                       available to help determine if a page is stable 
	 *                       already.  
	 * @param waitOnLocatorsTimeoutInMilliseconds how long at max the strategy should wait on 
	 *                              each locator
	 */
	public Page(ISeleniumUtil seleniumUtil, By[] waitOnLocators, 
			int waitOnLocatorsTimeoutInMilliseconds){
		this(seleniumUtil, 
			new WaitForPageToLoadByLocators(seleniumUtil, 
			waitOnLocators, waitOnLocatorsTimeoutInMilliseconds));
	}
	
	/**
	 * @param seleniumUtil selenium utility must be given
	 * @param waitOnLocators array of locators which are declared to be 
	 *                       available to help determine if a page is stable 
	 *                       already.  
	 *                              
	 */
	public Page(ISeleniumUtil seleniumUtil, By[] waitOnLocators){
		this(seleniumUtil, waitOnLocators, 
				SeleniumUtil.DEFAULT_TIMEOUT_IN_MILLISECONDS);
	}
	
	public void setSeleniumUtil(ISeleniumUtil seleniumUtil) {
		this.seleniumUtil = seleniumUtil;
	}
	
	/**
	 * Wait for page to load strategy interface.
	 */
	public static interface WaitForPageToLoadStrategy{
		public abstract void waitForPageToLoad();
	}
	
	
	/**
	 * Default wait strategy, not wait at all
	 */
	public static class WaitForPageToLoadNull 
		implements WaitForPageToLoadStrategy
	{
		@Override
		public void waitForPageToLoad() {
			// not to wait at all by default
		}
	}
	
	/** 
	 * Wait for page to load by the given locators strategy - if those 
	 * locators available, then page is loaded completely.
	 */
	public static class WaitForPageToLoadByLocators 
		implements WaitForPageToLoadStrategy
	{
		private ISeleniumUtil seleniumUtil;
		// determine on these locators if a page loaded successfully.
		private By[] waitOnLocators = new By[]{};
		// default wait time period is fine for most cases.
		private int waitOnLocatorsTimeoutInMilliseconds = 
				SeleniumUtil.DEFAULT_TIMEOUT_IN_MILLISECONDS;
		
		public WaitForPageToLoadByLocators(ISeleniumUtil seleniumUtil){
			this.seleniumUtil = seleniumUtil;
		}
		
		public WaitForPageToLoadByLocators(ISeleniumUtil seleniumUtil, 
				By[] waitOnLocators){
			this(seleniumUtil);
			this.waitOnLocators = waitOnLocators;
		}
		
		public WaitForPageToLoadByLocators(ISeleniumUtil seleniumUtil, 
				By[] waitOnLocators, int waitOnLocatorsTimeout){
			this(seleniumUtil, waitOnLocators);
			this.waitOnLocatorsTimeoutInMilliseconds = waitOnLocatorsTimeout;
		}

		@Override
		public final void waitForPageToLoad() 
				throws TimeoutException {
			// make sure all elements loaded on this page.
			seleniumUtil.waitOnElements(waitOnLocators, waitOnLocatorsTimeoutInMilliseconds);
		}
	}
	
	
	/*--------------------2. innate operations owned by this page-------------*/
	/*N/A*/
	
	
	/*--------------------3. reusable test oriented part, help to test--------*/
	
	/*--------------------4. utility classes----------------------------------*/
	/*N/A*/
}
